<?xml version="1.0" encoding="utf-8" standalone="no"?>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.1//EN"
        "http://www.w3.org/TR/xhtml11/DTD/xhtml11.dtd">

<html xmlns="http://www.w3.org/1999/xhtml">
<head>
    <title></title>
    <link href="../Styles/sure3.css" rel="stylesheet" type="text/css"/>
    <link href="../Styles/base1.css" rel="stylesheet" type="text/css"/>
</head>

<body>
<div class="markdown-body entry-content" itemprop="text">
    <h1><a href="#symbol"  class="anchor" id="user-content-symbol">

    </a>Symbol
    </h1>
    <h2><a href="#概述"  class="anchor" id="user-content-概述">

    </a>概述
    </h2>
    <p>ES5 的对象属性名都是字符串，这容易造成属性名的冲突。比如，你使用了一个他人提供的对象，但又想为这个对象添加新的方法（mixin
        模式），新方法的名字就有可能与现有方法产生冲突。如果有一种机制，保证每个属性的名字都是独一无二的就好了，这样就从根本上防止属性名的冲突。这就是 ES6 引入<code>Symbol</code>的原因。</p>
    <p>ES6 引入了一种新的原始数据类型<code>Symbol</code>，表示独一无二的值。它是 JavaScript
        语言的第七种数据类型，前六种是：<code>undefined</code>、<code>null</code>、布尔值（Boolean）、字符串（String）、数值（Number）、对象（Object）。</p>
    <p>Symbol 值通过<code>Symbol</code>函数生成。这就是说，对象的属性名现在可以有两种类型，一种是原来就有的字符串，另一种就是新增的 Symbol 类型。凡是属性名属于 Symbol
        类型，就都是独一无二的，可以保证不会与其他属性名产生冲突。</p>
    <div class="highlight highlight-source-js"><pre><span class="pl-k">let</span> s <span class="pl-k">=</span> <span
            class="pl-c1">Symbol</span>();

<span class="pl-k">typeof</span> s
<span class="pl-c"><span class="pl-c">//</span> "symbol"</span></pre>
    </div>
    <p>上面代码中，变量<code>s</code>就是一个独一无二的值。<code>typeof</code>运算符的结果，表明变量<code>s</code>是 Symbol 数据类型，而不是字符串之类的其他类型。</p>
    <p>注意，<code>Symbol</code>函数前不能使用<code>new</code>命令，否则会报错。这是因为生成的 Symbol 是一个原始类型的值，不是对象。也就是说，由于 Symbol
        值不是对象，所以不能添加属性。基本上，它是一种类似于字符串的数据类型。</p>
    <p><code>Symbol</code>函数可以接受一个字符串作为参数，表示对 Symbol 实例的描述，主要是为了在控制台显示，或者转为字符串时，比较容易区分。</p>
    <div class="highlight highlight-source-js"><pre><span class="pl-k">let</span> s1 <span class="pl-k">=</span> <span
            class="pl-c1">Symbol</span>(<span class="pl-s"><span class="pl-pds">'</span>foo<span class="pl-pds">'</span></span>);
<span class="pl-k">let</span> s2 <span class="pl-k">=</span> <span class="pl-c1">Symbol</span>(<span class="pl-s"><span
                class="pl-pds">'</span>bar<span class="pl-pds">'</span></span>);

s1 <span class="pl-c"><span class="pl-c">//</span> Symbol(foo)</span>
s2 <span class="pl-c"><span class="pl-c">//</span> Symbol(bar)</span>

<span class="pl-smi">s1</span>.<span class="pl-c1">toString</span>() <span class="pl-c"><span class="pl-c">//</span> "Symbol(foo)"</span>
<span class="pl-smi">s2</span>.<span class="pl-c1">toString</span>() <span class="pl-c"><span class="pl-c">//</span> "Symbol(bar)"</span></pre>
    </div>
    <p>上面代码中，<code>s1</code>和<code>s2</code>是两个 Symbol 值。如果不加参数，它们在控制台的输出都是<code>Symbol()</code>，不利于区分。有了参数以后，就等于为它们加上了描述，输出的时候就能够分清，到底是哪一个值。
    </p>
    <p>如果 Symbol 的参数是一个对象，就会调用该对象的<code>toString</code>方法，将其转为字符串，然后才生成一个 Symbol 值。</p>
    <div class="highlight highlight-source-js"><pre><span class="pl-k">const</span> <span class="pl-c1">obj</span> <span
            class="pl-k">=</span> {
  <span class="pl-en">toString</span>() {
    <span class="pl-k">return</span> <span class="pl-s"><span class="pl-pds">'</span>abc<span
                class="pl-pds">'</span></span>;
  }
};
<span class="pl-k">const</span> <span class="pl-c1">sym</span> <span class="pl-k">=</span> <span
                class="pl-c1">Symbol</span>(obj);
sym <span class="pl-c"><span class="pl-c">//</span> Symbol(abc)</span></pre>
    </div>
    <p>注意，<code>Symbol</code>函数的参数只是表示对当前 Symbol 值的描述，因此相同参数的<code>Symbol</code>函数的返回值是不相等的。</p>
    <div class="highlight highlight-source-js"><pre><span class="pl-c"><span class="pl-c">//</span> 没有参数的情况</span>
<span class="pl-k">let</span> s1 <span class="pl-k">=</span> <span class="pl-c1">Symbol</span>();
<span class="pl-k">let</span> s2 <span class="pl-k">=</span> <span class="pl-c1">Symbol</span>();

s1 <span class="pl-k">===</span> s2 <span class="pl-c"><span class="pl-c">//</span> false</span>

<span class="pl-c"><span class="pl-c">//</span> 有参数的情况</span>
<span class="pl-k">let</span> s1 <span class="pl-k">=</span> <span class="pl-c1">Symbol</span>(<span class="pl-s"><span
                class="pl-pds">'</span>foo<span class="pl-pds">'</span></span>);
<span class="pl-k">let</span> s2 <span class="pl-k">=</span> <span class="pl-c1">Symbol</span>(<span class="pl-s"><span
                class="pl-pds">'</span>foo<span class="pl-pds">'</span></span>);

s1 <span class="pl-k">===</span> s2 <span class="pl-c"><span class="pl-c">//</span> false</span></pre>
    </div>
    <p>上面代码中，<code>s1</code>和<code>s2</code>都是<code>Symbol</code>函数的返回值，而且参数相同，但是它们是不相等的。</p>
    <p>Symbol 值不能与其他类型的值进行运算，会报错。</p>
    <div class="highlight highlight-source-js"><pre><span class="pl-k">let</span> sym <span class="pl-k">=</span> <span
            class="pl-c1">Symbol</span>(<span class="pl-s"><span class="pl-pds">'</span>My symbol<span
            class="pl-pds">'</span></span>);

<span class="pl-s"><span class="pl-pds">"</span>your symbol is <span class="pl-pds">"</span></span> <span
                class="pl-k">+</span> sym
<span class="pl-c"><span class="pl-c">//</span> TypeError: can't convert symbol to string</span>
<span class="pl-s"><span class="pl-pds">`</span>your symbol is <span class="pl-s1"><span
        class="pl-pse">${</span>sym<span class="pl-pse">}</span></span><span class="pl-pds">`</span></span>
<span class="pl-c"><span class="pl-c">//</span> TypeError: can't convert symbol to string</span></pre>
    </div>
    <p>但是，Symbol 值可以显式转为字符串。</p>
    <div class="highlight highlight-source-js"><pre><span class="pl-k">let</span> sym <span class="pl-k">=</span> <span
            class="pl-c1">Symbol</span>(<span class="pl-s"><span class="pl-pds">'</span>My symbol<span
            class="pl-pds">'</span></span>);

<span class="pl-c1">String</span>(sym) <span class="pl-c"><span class="pl-c">//</span> 'Symbol(My symbol)'</span>
<span class="pl-smi">sym</span>.<span class="pl-c1">toString</span>() <span class="pl-c"><span class="pl-c">//</span> 'Symbol(My symbol)'</span></pre>
    </div>
    <p>另外，Symbol 值也可以转为布尔值，但是不能转为数值。</p>
    <div class="highlight highlight-source-js"><pre><span class="pl-k">let</span> sym <span class="pl-k">=</span> <span
            class="pl-c1">Symbol</span>();
<span class="pl-c1">Boolean</span>(sym) <span class="pl-c"><span class="pl-c">//</span> true</span>
<span class="pl-k">!</span>sym  <span class="pl-c"><span class="pl-c">//</span> false</span>

<span class="pl-k">if</span> (sym) {
  <span class="pl-c"><span class="pl-c">//</span> ...</span>
}

<span class="pl-c1">Number</span>(sym) <span class="pl-c"><span class="pl-c">//</span> TypeError</span>
sym <span class="pl-k">+</span> <span class="pl-c1">2</span> <span class="pl-c"><span
                class="pl-c">//</span> TypeError</span></pre>
    </div>
    <h2><a href="#作为属性名的-symbol"  class="anchor" id="user-content-作为属性名的-symbol">

    </a>作为属性名的 Symbol
    </h2>
    <p>由于每一个 Symbol 值都是不相等的，这意味着 Symbol 值可以作为标识符，用于对象的属性名，就能保证不会出现同名的属性。这对于一个对象由多个模块构成的情况非常有用，能防止某一个键被不小心改写或覆盖。</p>
    <div class="highlight highlight-source-js"><pre><span class="pl-k">let</span> mySymbol <span
            class="pl-k">=</span> <span class="pl-c1">Symbol</span>();

<span class="pl-c"><span class="pl-c">//</span> 第一种写法</span>
<span class="pl-k">let</span> a <span class="pl-k">=</span> {};
a[mySymbol] <span class="pl-k">=</span> <span class="pl-s"><span class="pl-pds">'</span>Hello!<span
                class="pl-pds">'</span></span>;

<span class="pl-c"><span class="pl-c">//</span> 第二种写法</span>
<span class="pl-k">let</span> a <span class="pl-k">=</span> {
  [mySymbol]<span class="pl-k">:</span> <span class="pl-s"><span class="pl-pds">'</span>Hello!<span
                class="pl-pds">'</span></span>
};

<span class="pl-c"><span class="pl-c">//</span> 第三种写法</span>
<span class="pl-k">let</span> a <span class="pl-k">=</span> {};
<span class="pl-c1">Object</span>.<span class="pl-en">defineProperty</span>(a, mySymbol, { value<span
                class="pl-k">:</span> <span class="pl-s"><span class="pl-pds">'</span>Hello!<span
                class="pl-pds">'</span></span> });

<span class="pl-c"><span class="pl-c">//</span> 以上写法都得到同样结果</span>
a[mySymbol] <span class="pl-c"><span class="pl-c">//</span> "Hello!"</span></pre>
    </div>
    <p>上面代码通过方括号结构和<code>Object.defineProperty</code>，将对象的属性名指定为一个 Symbol 值。</p>
    <p>注意，Symbol 值作为对象属性名时，不能用点运算符。</p>
    <div class="highlight highlight-source-js"><pre><span class="pl-k">const</span> <span class="pl-c1">mySymbol</span> <span
            class="pl-k">=</span> <span class="pl-c1">Symbol</span>();
<span class="pl-k">const</span> <span class="pl-c1">a</span> <span class="pl-k">=</span> {};

<span class="pl-smi">a</span>.<span class="pl-smi">mySymbol</span> <span class="pl-k">=</span> <span class="pl-s"><span
                class="pl-pds">'</span>Hello!<span class="pl-pds">'</span></span>;
a[mySymbol] <span class="pl-c"><span class="pl-c">//</span> undefined</span>
a[<span class="pl-s"><span class="pl-pds">'</span>mySymbol<span class="pl-pds">'</span></span>] <span class="pl-c"><span
                class="pl-c">//</span> "Hello!"</span></pre>
    </div>
    <p>上面代码中，因为点运算符后面总是字符串，所以不会读取<code>mySymbol</code>作为标识名所指代的那个值，导致<code>a</code>的属性名实际上是一个字符串，而不是一个 Symbol 值。</p>
    <p>同理，在对象的内部，使用 Symbol 值定义属性时，Symbol 值必须放在方括号之中。</p>
    <div class="highlight highlight-source-js"><pre><span class="pl-k">let</span> s <span class="pl-k">=</span> <span
            class="pl-c1">Symbol</span>();

<span class="pl-k">let</span> obj <span class="pl-k">=</span> {
  [s]<span class="pl-k">:</span> <span class="pl-k">function</span> (<span class="pl-smi">arg</span>) { <span
                class="pl-k">...</span> }
};

obj[s](<span class="pl-c1">123</span>);</pre>
    </div>
    <p>上面代码中，如果<code>s</code>不放在方括号中，该属性的键名就是字符串<code>s</code>，而不是<code>s</code>所代表的那个 Symbol 值。</p>
    <p>采用增强的对象写法，上面代码的<code>obj</code>对象可以写得更简洁一些。</p>
    <div class="highlight highlight-source-js"><pre><span class="pl-k">let</span> obj <span class="pl-k">=</span> {
  [<span class="pl-v">s</span>](<span class="pl-smi">arg</span>) { <span class="pl-k">...</span> }
};</pre>
    </div>
    <p>Symbol 类型还可以用于定义一组常量，保证这组常量的值都是不相等的。</p>
    <div class="highlight highlight-source-js"><pre><span class="pl-smi">log</span>.<span
            class="pl-smi">levels</span> <span class="pl-k">=</span> {
  <span class="pl-c1">DEBUG</span><span class="pl-k">:</span> <span class="pl-c1">Symbol</span>(<span class="pl-s"><span
                class="pl-pds">'</span>debug<span class="pl-pds">'</span></span>),
  <span class="pl-c1">INFO</span><span class="pl-k">:</span> <span class="pl-c1">Symbol</span>(<span class="pl-s"><span
                class="pl-pds">'</span>info<span class="pl-pds">'</span></span>),
  <span class="pl-c1">WARN</span><span class="pl-k">:</span> <span class="pl-c1">Symbol</span>(<span class="pl-s"><span
                class="pl-pds">'</span>warn<span class="pl-pds">'</span></span>)
};
<span class="pl-en">log</span>(<span class="pl-smi">log</span>.<span class="pl-smi">levels</span>.<span class="pl-c1">DEBUG</span>, <span
                class="pl-s"><span class="pl-pds">'</span>debug message<span class="pl-pds">'</span></span>);
<span class="pl-en">log</span>(<span class="pl-smi">log</span>.<span class="pl-smi">levels</span>.<span class="pl-c1">INFO</span>, <span
                class="pl-s"><span class="pl-pds">'</span>info message<span class="pl-pds">'</span></span>);</pre>
    </div>
    <p>下面是另外一个例子。</p>
    <div class="highlight highlight-source-js"><pre><span class="pl-k">const</span> <span class="pl-c1">COLOR_RED</span>    <span
            class="pl-k">=</span> <span class="pl-c1">Symbol</span>();
<span class="pl-k">const</span> <span class="pl-c1">COLOR_GREEN</span>  <span class="pl-k">=</span> <span class="pl-c1">Symbol</span>();

<span class="pl-k">function</span> <span class="pl-en">getComplement</span>(<span class="pl-smi">color</span>) {
  <span class="pl-k">switch</span> (color) {
    <span class="pl-k">case</span> <span class="pl-c1">COLOR_RED</span>:
      <span class="pl-k">return</span> <span class="pl-c1">COLOR_GREEN</span>;
    <span class="pl-k">case</span> <span class="pl-c1">COLOR_GREEN</span>:
      <span class="pl-k">return</span> <span class="pl-c1">COLOR_RED</span>;
    <span class="pl-k">default</span>:
      <span class="pl-k">throw</span> <span class="pl-k">new</span> <span class="pl-en">Error</span>(<span class="pl-s"><span
                class="pl-pds">'</span>Undefined color<span class="pl-pds">'</span></span>);
    }
}</pre>
    </div>
    <p>常量使用 Symbol 值最大的好处，就是其他任何值都不可能有相同的值了，因此可以保证上面的<code>switch</code>语句会按设计的方式工作。</p>
    <p>还有一点需要注意，Symbol 值作为属性名时，该属性还是公开属性，不是私有属性。</p>
    <h2><a href="#实例消除魔术字符串"  class="anchor" id="user-content-实例消除魔术字符串">

    </a>实例：消除魔术字符串
    </h2>
    <p>魔术字符串指的是，在代码之中多次出现、与代码形成强耦合的某一个具体的字符串或者数值。风格良好的代码，应该尽量消除魔术字符串，改由含义清晰的变量代替。</p>
    <div class="highlight highlight-source-js"><pre><span class="pl-k">function</span> <span
            class="pl-en">getArea</span>(<span class="pl-smi">shape</span>, <span class="pl-smi">options</span>) {
  <span class="pl-k">let</span> area <span class="pl-k">=</span> <span class="pl-c1">0</span>;

  <span class="pl-k">switch</span> (shape) {
    <span class="pl-k">case</span> <span class="pl-s"><span class="pl-pds">'</span>Triangle<span class="pl-pds">'</span></span>: <span
                class="pl-c"><span class="pl-c">//</span> 魔术字符串</span>
      area <span class="pl-k">=</span> <span class="pl-c1">.5</span> <span class="pl-k">*</span> <span class="pl-smi">options</span>.<span
                class="pl-c1">width</span> <span class="pl-k">*</span> <span class="pl-smi">options</span>.<span
                class="pl-c1">height</span>;
      <span class="pl-k">break</span>;
    <span class="pl-c"><span class="pl-c">/*</span> ... more code ... <span class="pl-c">*/</span></span>
  }

  <span class="pl-k">return</span> area;
}

<span class="pl-en">getArea</span>(<span class="pl-s"><span class="pl-pds">'</span>Triangle<span class="pl-pds">'</span></span>, { width<span
                class="pl-k">:</span> <span class="pl-c1">100</span>, height<span class="pl-k">:</span> <span
                class="pl-c1">100</span> }); <span class="pl-c"><span class="pl-c">//</span> 魔术字符串</span></pre>
    </div>
    <p>上面代码中，字符串<code>Triangle</code>就是一个魔术字符串。它多次出现，与代码形成“强耦合”，不利于将来的修改和维护。</p>
    <p>常用的消除魔术字符串的方法，就是把它写成一个变量。</p>
    <div class="highlight highlight-source-js"><pre><span class="pl-k">const</span> <span class="pl-c1">shapeType</span> <span
            class="pl-k">=</span> {
  triangle<span class="pl-k">:</span> <span class="pl-s"><span class="pl-pds">'</span>Triangle<span
                class="pl-pds">'</span></span>
};

<span class="pl-k">function</span> <span class="pl-en">getArea</span>(<span class="pl-smi">shape</span>, <span
                class="pl-smi">options</span>) {
  <span class="pl-k">let</span> area <span class="pl-k">=</span> <span class="pl-c1">0</span>;
  <span class="pl-k">switch</span> (shape) {
    <span class="pl-k">case</span> <span class="pl-smi">shapeType</span>.<span class="pl-smi">triangle</span>:
      area <span class="pl-k">=</span> <span class="pl-c1">.5</span> <span class="pl-k">*</span> <span class="pl-smi">options</span>.<span
                class="pl-c1">width</span> <span class="pl-k">*</span> <span class="pl-smi">options</span>.<span
                class="pl-c1">height</span>;
      <span class="pl-k">break</span>;
  }
  <span class="pl-k">return</span> area;
}

<span class="pl-en">getArea</span>(<span class="pl-smi">shapeType</span>.<span
                class="pl-smi">triangle</span>, { width<span class="pl-k">:</span> <span class="pl-c1">100</span>, height<span
                class="pl-k">:</span> <span class="pl-c1">100</span> });</pre>
    </div>
    <p>上面代码中，我们把<code>Triangle</code>写成<code>shapeType</code>对象的<code>triangle</code>属性，这样就消除了强耦合。</p>
    <p>如果仔细分析，可以发现<code>shapeType.triangle</code>等于哪个值并不重要，只要确保不会跟其他<code>shapeType</code>属性的值冲突即可。因此，这里就很适合改用 Symbol 值。
    </p>
    <div class="highlight highlight-source-js"><pre><span class="pl-k">const</span> <span class="pl-c1">shapeType</span> <span
            class="pl-k">=</span> {
  triangle<span class="pl-k">:</span> <span class="pl-c1">Symbol</span>()
};</pre>
    </div>
    <p>上面代码中，除了将<code>shapeType.triangle</code>的值设为一个 Symbol，其他地方都不用修改。</p>
    <h2><a href="#属性名的遍历"  class="anchor" id="user-content-属性名的遍历">

    </a>属性名的遍历
    </h2>
    <p>Symbol 作为属性名，该属性不会出现在<code>for...in</code>、<code>for...of</code>循环中，也不会被<code>Object.keys()</code>、<code>Object.getOwnPropertyNames()</code>、<code>JSON.stringify()</code>返回。但是，它也不是私有属性，有一个<code>Object.getOwnPropertySymbols</code>方法，可以获取指定对象的所有
        Symbol 属性名。</p>
    <p><code>Object.getOwnPropertySymbols</code>方法返回一个数组，成员是当前对象的所有用作属性名的 Symbol 值。</p>
    <div class="highlight highlight-source-js"><pre><span class="pl-k">const</span> <span class="pl-c1">obj</span> <span
            class="pl-k">=</span> {};
<span class="pl-k">let</span> a <span class="pl-k">=</span> <span class="pl-c1">Symbol</span>(<span class="pl-s"><span
                class="pl-pds">'</span>a<span class="pl-pds">'</span></span>);
<span class="pl-k">let</span> b <span class="pl-k">=</span> <span class="pl-c1">Symbol</span>(<span class="pl-s"><span
                class="pl-pds">'</span>b<span class="pl-pds">'</span></span>);

obj[a] <span class="pl-k">=</span> <span class="pl-s"><span class="pl-pds">'</span>Hello<span
                class="pl-pds">'</span></span>;
obj[b] <span class="pl-k">=</span> <span class="pl-s"><span class="pl-pds">'</span>World<span
                class="pl-pds">'</span></span>;

<span class="pl-k">const</span> <span class="pl-c1">objectSymbols</span> <span class="pl-k">=</span> <span
                class="pl-c1">Object</span>.<span class="pl-en">getOwnPropertySymbols</span>(obj);

objectSymbols
<span class="pl-c"><span class="pl-c">//</span> [Symbol(a), Symbol(b)]</span></pre>
    </div>
    <p>
        下面是另一个例子，<code>Object.getOwnPropertySymbols</code>方法与<code>for...in</code>循环、<code>Object.getOwnPropertyNames</code>方法进行对比的例子。
    </p>
    <div class="highlight highlight-source-js"><pre><span class="pl-k">const</span> <span class="pl-c1">obj</span> <span
            class="pl-k">=</span> {};

<span class="pl-k">let</span> foo <span class="pl-k">=</span> <span class="pl-c1">Symbol</span>(<span class="pl-s"><span
                class="pl-pds">"</span>foo<span class="pl-pds">"</span></span>);

<span class="pl-c1">Object</span>.<span class="pl-en">defineProperty</span>(obj, foo, {
  value<span class="pl-k">:</span> <span class="pl-s"><span class="pl-pds">"</span>foobar<span
                class="pl-pds">"</span></span>,
});

<span class="pl-k">for</span> (<span class="pl-k">let</span> i <span class="pl-k">in</span> obj) {
  <span class="pl-en">console</span>.<span class="pl-c1">log</span>(i); <span class="pl-c"><span class="pl-c">//</span> 无输出</span>
}

<span class="pl-c1">Object</span>.<span class="pl-en">getOwnPropertyNames</span>(obj)
<span class="pl-c"><span class="pl-c">//</span> []</span>

<span class="pl-c1">Object</span>.<span class="pl-en">getOwnPropertySymbols</span>(obj)
<span class="pl-c"><span class="pl-c">//</span> [Symbol(foo)]</span></pre>
    </div>
    <p>
        上面代码中，使用<code>Object.getOwnPropertyNames</code>方法得不到<code>Symbol</code>属性名，需要使用<code>Object.getOwnPropertySymbols</code>方法。
    </p>
    <p>另一个新的 API，<code>Reflect.ownKeys</code>方法可以返回所有类型的键名，包括常规键名和 Symbol 键名。</p>
    <div class="highlight highlight-source-js"><pre><span class="pl-k">let</span> obj <span class="pl-k">=</span> {
  [<span class="pl-c1">Symbol</span>(<span class="pl-s"><span class="pl-pds">'</span>my_key<span class="pl-pds">'</span></span>)]<span
                class="pl-k">:</span> <span class="pl-c1">1</span>,
  enum<span class="pl-k">:</span> <span class="pl-c1">2</span>,
  nonEnum<span class="pl-k">:</span> <span class="pl-c1">3</span>
};

<span class="pl-c1">Reflect</span>.<span class="pl-en">ownKeys</span>(obj)
<span class="pl-c"><span class="pl-c">//</span>  ["enum", "nonEnum", Symbol(my_key)]</span></pre>
    </div>
    <p>由于以 Symbol 值作为名称的属性，不会被常规方法遍历得到。我们可以利用这个特性，为对象定义一些非私有的、但又希望只用于内部的方法。</p>
    <div class="highlight highlight-source-js"><pre><span class="pl-k">let</span> size <span class="pl-k">=</span> <span
            class="pl-c1">Symbol</span>(<span class="pl-s"><span class="pl-pds">'</span>size<span
            class="pl-pds">'</span></span>);

<span class="pl-k">class</span> <span class="pl-en">Collection</span> {
  <span class="pl-en">constructor</span>() {
    <span class="pl-c1">this</span>[size] <span class="pl-k">=</span> <span class="pl-c1">0</span>;
  }

  <span class="pl-en">add</span>(<span class="pl-smi">item</span>) {
    <span class="pl-c1">this</span>[<span class="pl-c1">this</span>[size]] <span class="pl-k">=</span> item;
    <span class="pl-c1">this</span>[size]<span class="pl-k">++</span>;
  }

  <span class="pl-k">static</span> <span class="pl-en">sizeOf</span>(<span class="pl-smi">instance</span>) {
    <span class="pl-k">return</span> instance[size];
  }
}

<span class="pl-k">let</span> x <span class="pl-k">=</span> <span class="pl-k">new</span> <span
                class="pl-en">Collection</span>();
<span class="pl-smi">Collection</span>.<span class="pl-en">sizeOf</span>(x) <span class="pl-c"><span
                class="pl-c">//</span> 0</span>

<span class="pl-smi">x</span>.<span class="pl-c1">add</span>(<span class="pl-s"><span class="pl-pds">'</span>foo<span
                class="pl-pds">'</span></span>);
<span class="pl-smi">Collection</span>.<span class="pl-en">sizeOf</span>(x) <span class="pl-c"><span
                class="pl-c">//</span> 1</span>

<span class="pl-c1">Object</span>.<span class="pl-c1">keys</span>(x) <span class="pl-c"><span class="pl-c">//</span> ['0']</span>
<span class="pl-c1">Object</span>.<span class="pl-en">getOwnPropertyNames</span>(x) <span class="pl-c"><span
                class="pl-c">//</span> ['0']</span>
<span class="pl-c1">Object</span>.<span class="pl-en">getOwnPropertySymbols</span>(x) <span class="pl-c"><span
                class="pl-c">//</span> [Symbol(size)]</span></pre>
    </div>
    <p>上面代码中，对象<code>x</code>的<code>size</code>属性是一个 Symbol 值，所以<code>Object.keys(x)</code>、<code>Object.getOwnPropertyNames(x)</code>都无法获取它。这就造成了一种非私有的内部方法的效果。
    </p>
    <h2><a href="#symbolforsymbolkeyfor"  class="anchor" id="user-content-symbolforsymbolkeyfor">

    </a>Symbol.for()，Symbol.keyFor()
    </h2>
    <p>有时，我们希望重新使用同一个 Symbol 值，<code>Symbol.for</code>方法可以做到这一点。它接受一个字符串作为参数，然后搜索有没有以该参数作为名称的 Symbol 值。如果有，就返回这个 Symbol
        值，否则就新建并返回一个以该字符串为名称的 Symbol 值。</p>
    <div class="highlight highlight-source-js"><pre><span class="pl-k">let</span> s1 <span class="pl-k">=</span> <span
            class="pl-c1">Symbol</span>.<span class="pl-en">for</span>(<span class="pl-s"><span class="pl-pds">'</span>foo<span
            class="pl-pds">'</span></span>);
<span class="pl-k">let</span> s2 <span class="pl-k">=</span> <span class="pl-c1">Symbol</span>.<span
                class="pl-en">for</span>(<span class="pl-s"><span class="pl-pds">'</span>foo<span
                class="pl-pds">'</span></span>);

s1 <span class="pl-k">===</span> s2 <span class="pl-c"><span class="pl-c">//</span> true</span></pre>
    </div>
    <p>上面代码中，<code>s1</code>和<code>s2</code>都是 Symbol 值，但是它们都是同样参数的<code>Symbol.for</code>方法生成的，所以实际上是同一个值。</p>
    <p><code>Symbol.for()</code>与<code>Symbol()</code>这两种写法，都会生成新的
        Symbol。它们的区别是，前者会被登记在全局环境中供搜索，后者不会。<code>Symbol.for()</code>不会每次调用就返回一个新的 Symbol 类型的值，而是会先检查给定的<code>key</code>是否已经存在，如果不存在才会新建一个值。比如，如果你调用<code>Symbol.for("cat")</code>30
        次，每次都会返回同一个 Symbol 值，但是调用<code>Symbol("cat")</code>30 次，会返回 30 个不同的 Symbol 值。</p>
    <div class="highlight highlight-source-js"><pre><span class="pl-c1">Symbol</span>.<span
            class="pl-en">for</span>(<span class="pl-s"><span class="pl-pds">"</span>bar<span
            class="pl-pds">"</span></span>) <span class="pl-k">===</span> <span class="pl-c1">Symbol</span>.<span
            class="pl-en">for</span>(<span class="pl-s"><span class="pl-pds">"</span>bar<span
            class="pl-pds">"</span></span>)
<span class="pl-c"><span class="pl-c">//</span> true</span>

<span class="pl-c1">Symbol</span>(<span class="pl-s"><span class="pl-pds">"</span>bar<span
                class="pl-pds">"</span></span>) <span class="pl-k">===</span> <span class="pl-c1">Symbol</span>(<span
                class="pl-s"><span class="pl-pds">"</span>bar<span class="pl-pds">"</span></span>)
<span class="pl-c"><span class="pl-c">//</span> false</span></pre>
    </div>
    <p>上面代码中，由于<code>Symbol()</code>写法没有登记机制，所以每次调用都会返回一个不同的值。</p>
    <p><code>Symbol.keyFor</code>方法返回一个已登记的 Symbol 类型值的<code>key</code>。</p>
    <div class="highlight highlight-source-js"><pre><span class="pl-k">let</span> s1 <span class="pl-k">=</span> <span
            class="pl-c1">Symbol</span>.<span class="pl-en">for</span>(<span class="pl-s"><span class="pl-pds">"</span>foo<span
            class="pl-pds">"</span></span>);
<span class="pl-c1">Symbol</span>.<span class="pl-en">keyFor</span>(s1) <span class="pl-c"><span class="pl-c">//</span> "foo"</span>

<span class="pl-k">let</span> s2 <span class="pl-k">=</span> <span class="pl-c1">Symbol</span>(<span class="pl-s"><span
                class="pl-pds">"</span>foo<span class="pl-pds">"</span></span>);
<span class="pl-c1">Symbol</span>.<span class="pl-en">keyFor</span>(s2) <span class="pl-c"><span class="pl-c">//</span> undefined</span></pre>
    </div>
    <p>上面代码中，变量<code>s2</code>属于未登记的 Symbol 值，所以返回<code>undefined</code>。</p>
    <p>需要注意的是，<code>Symbol.for</code>为 Symbol 值登记的名字，是全局环境的，可以在不同的 iframe 或 service worker 中取到同一个值。</p>
    <div class="highlight highlight-source-js"><pre>iframe <span class="pl-k">=</span> <span
            class="pl-c1">document</span>.<span class="pl-c1">createElement</span>(<span class="pl-s"><span
            class="pl-pds">'</span>iframe<span class="pl-pds">'</span></span>);
<span class="pl-smi">iframe</span>.<span class="pl-smi">src</span> <span class="pl-k">=</span> <span class="pl-c1">String</span>(<span
                class="pl-c1">window</span>.<span class="pl-c1">location</span>);
<span class="pl-c1">document</span>.<span class="pl-c1">body</span>.<span class="pl-c1">appendChild</span>(iframe);

<span class="pl-smi">iframe</span>.<span class="pl-smi">contentWindow</span>.<span class="pl-smi">Symbol</span>.<span
                class="pl-en">for</span>(<span class="pl-s"><span class="pl-pds">'</span>foo<span
                class="pl-pds">'</span></span>) <span class="pl-k">===</span> <span class="pl-c1">Symbol</span>.<span
                class="pl-en">for</span>(<span class="pl-s"><span class="pl-pds">'</span>foo<span
                class="pl-pds">'</span></span>)
<span class="pl-c"><span class="pl-c">//</span> true</span></pre>
    </div>
    <p>上面代码中，iframe 窗口生成的 Symbol 值，可以在主页面得到。</p>
    <h2><a href="#实例模块的-singleton-模式"  class="anchor" id="user-content-实例模块的-singleton-模式">

    </a>实例：模块的 Singleton 模式
    </h2>
    <p>Singleton 模式指的是调用一个类，任何时候返回的都是同一个实例。</p>
    <p>对于 Node 来说，模块文件可以看成是一个类。怎么保证每次执行这个模块文件，返回的都是同一个实例呢？</p>
    <p>很容易想到，可以把实例放到顶层对象<code>global</code>。</p>
    <div class="highlight highlight-source-js"><pre><span class="pl-c"><span class="pl-c">//</span> mod.js</span>
<span class="pl-k">function</span> <span class="pl-en">A</span>() {
  <span class="pl-c1">this</span>.<span class="pl-smi">foo</span> <span class="pl-k">=</span> <span class="pl-s"><span
                class="pl-pds">'</span>hello<span class="pl-pds">'</span></span>;
}

<span class="pl-k">if</span> (<span class="pl-k">!</span><span class="pl-c1">global</span>.<span
                class="pl-smi">_foo</span>) {
  <span class="pl-c1">global</span>.<span class="pl-smi">_foo</span> <span class="pl-k">=</span> <span
                class="pl-k">new</span> <span class="pl-en">A</span>();
}

<span class="pl-c1">module</span>.<span class="pl-smi">exports</span> <span class="pl-k">=</span> <span class="pl-c1">global</span>.<span
                class="pl-smi">_foo</span>;</pre>
    </div>
    <p>然后，加载上面的<code>mod.js</code>。</p>
    <div class="highlight highlight-source-js"><pre><span class="pl-k">const</span> <span class="pl-c1">a</span> <span
            class="pl-k">=</span> <span class="pl-c1">require</span>(<span class="pl-s"><span class="pl-pds">'</span>./mod.js<span
            class="pl-pds">'</span></span>);
<span class="pl-en">console</span>.<span class="pl-c1">log</span>(<span class="pl-smi">a</span>.<span
                class="pl-smi">foo</span>);</pre>
    </div>
    <p>上面代码中，变量<code>a</code>任何时候加载的都是<code>A</code>的同一个实例。</p>
    <p>但是，这里有一个问题，全局变量<code>global._foo</code>是可写的，任何文件都可以修改。</p>
    <div class="highlight highlight-source-js"><pre><span class="pl-k">const</span> <span class="pl-c1">a</span> <span
            class="pl-k">=</span> <span class="pl-c1">require</span>(<span class="pl-s"><span class="pl-pds">'</span>./mod.js<span
            class="pl-pds">'</span></span>);
<span class="pl-c1">global</span>.<span class="pl-smi">_foo</span> <span class="pl-k">=</span> <span
                class="pl-c1">123</span>;</pre>
    </div>
    <p>上面的代码，会使得别的脚本加载<code>mod.js</code>都失真。</p>
    <p>为了防止这种情况出现，我们就可以使用 Symbol。</p>
    <div class="highlight highlight-source-js"><pre><span class="pl-c"><span class="pl-c">//</span> mod.js</span>
<span class="pl-k">const</span> <span class="pl-c1">FOO_KEY</span> <span class="pl-k">=</span> <span class="pl-c1">Symbol</span>.<span
                class="pl-en">for</span>(<span class="pl-s"><span class="pl-pds">'</span>foo<span
                class="pl-pds">'</span></span>);

<span class="pl-k">function</span> <span class="pl-en">A</span>() {
  <span class="pl-c1">this</span>.<span class="pl-smi">foo</span> <span class="pl-k">=</span> <span class="pl-s"><span
                class="pl-pds">'</span>hello<span class="pl-pds">'</span></span>;
}

<span class="pl-k">if</span> (<span class="pl-k">!</span><span class="pl-c1">global</span>[<span
                class="pl-c1">FOO_KEY</span>]) {
  <span class="pl-c1">global</span>[<span class="pl-c1">FOO_KEY</span>] <span class="pl-k">=</span> <span class="pl-k">new</span> <span
                class="pl-en">A</span>();
}

<span class="pl-c1">module</span>.<span class="pl-smi">exports</span> <span class="pl-k">=</span> <span class="pl-c1">global</span>[<span
                class="pl-c1">FOO_KEY</span>];</pre>
    </div>
    <p>上面代码中，可以保证<code>global[FOO_KEY]</code>不会被无意间覆盖，但还是可以被改写。</p>
    <div class="highlight highlight-source-js"><pre><span class="pl-k">const</span> <span class="pl-c1">a</span> <span
            class="pl-k">=</span> <span class="pl-c1">require</span>(<span class="pl-s"><span class="pl-pds">'</span>./mod.js<span
            class="pl-pds">'</span></span>);
<span class="pl-c1">global</span>[<span class="pl-c1">Symbol</span>.<span class="pl-en">for</span>(<span
                class="pl-s"><span class="pl-pds">'</span>foo<span class="pl-pds">'</span></span>)] <span
                class="pl-k">=</span> <span class="pl-c1">123</span>;</pre>
    </div>
    <p>如果键名使用<code>Symbol</code>方法生成，那么外部将无法引用这个值，当然也就无法改写。</p>
    <div class="highlight highlight-source-js"><pre><span class="pl-c"><span class="pl-c">//</span> mod.js</span>
<span class="pl-k">const</span> <span class="pl-c1">FOO_KEY</span> <span class="pl-k">=</span> <span class="pl-c1">Symbol</span>(<span
                class="pl-s"><span class="pl-pds">'</span>foo<span class="pl-pds">'</span></span>);

<span class="pl-c"><span class="pl-c">//</span> 后面代码相同 ……</span></pre>
    </div>
    <p>上面代码将导致其他脚本都无法引用<code>FOO_KEY</code>。但这样也有一个问题，就是如果多次执行这个脚本，每次得到的<code>FOO_KEY</code>都是不一样的。虽然 Node
        会将脚本的执行结果缓存，一般情况下，不会多次执行同一个脚本，但是用户可以手动清除缓存，所以也不是完全可靠。</p>
    <h2><a href="#内置的-symbol-值"  class="anchor" id="user-content-内置的-symbol-值">

    </a>内置的 Symbol 值
    </h2>
    <p>除了定义自己使用的 Symbol 值以外，ES6 还提供了 11 个内置的 Symbol 值，指向语言内部使用的方法。</p>
    <h3><a href="#symbolhasinstance"  class="anchor" id="user-content-symbolhasinstance">

    </a>Symbol.hasInstance
    </h3>
    <p>对象的<code>Symbol.hasInstance</code>属性，指向一个内部方法。当其他对象使用<code>instanceof</code>运算符，判断是否为该对象的实例时，会调用这个方法。比如，<code>foo
        instanceof Foo</code>在语言内部，实际调用的是<code>Foo[Symbol.hasInstance](foo)</code>。</p>
    <div class="highlight highlight-source-js"><pre><span class="pl-k">class</span> <span class="pl-en">MyClass</span> {
  [<span class="pl-c1">Symbol</span>.<span class="pl-smi">hasInstance</span>](<span class="pl-smi">foo</span>) {
    <span class="pl-k">return</span> foo <span class="pl-k">instanceof</span> <span class="pl-c1">Array</span>;
  }
}

[<span class="pl-c1">1</span>, <span class="pl-c1">2</span>, <span class="pl-c1">3</span>] <span
                class="pl-k">instanceof</span> <span class="pl-k">new</span> <span class="pl-en">MyClass</span>() <span
                class="pl-c"><span class="pl-c">//</span> true</span></pre>
    </div>
    <p>上面代码中，<code>MyClass</code>是一个类，<code>new MyClass()</code>会返回一个实例。该实例的<code>Symbol.hasInstance</code>方法，会在进行<code>instanceof</code>运算时自动调用，判断左侧的运算子是否为<code>Array</code>的实例。
    </p>
    <p>下面是另一个例子。</p>
    <div class="highlight highlight-source-js"><pre><span class="pl-k">class</span> <span class="pl-en">Even</span> {
  <span class="pl-k">static</span> [<span class="pl-c1">Symbol</span>.<span class="pl-smi">hasInstance</span>](<span
                class="pl-smi">obj</span>) {
    <span class="pl-k">return</span> <span class="pl-c1">Number</span>(obj) <span class="pl-k">%</span> <span
                class="pl-c1">2</span> <span class="pl-k">===</span> <span class="pl-c1">0</span>;
  }
}

<span class="pl-c"><span class="pl-c">//</span> 等同于</span>
<span class="pl-k">const</span> <span class="pl-c1">Even</span> <span class="pl-k">=</span> {
  [<span class="pl-c1">Symbol</span>.<span class="pl-smi">hasInstance</span>](<span class="pl-smi">obj</span>) {
    <span class="pl-k">return</span> <span class="pl-c1">Number</span>(obj) <span class="pl-k">%</span> <span
                class="pl-c1">2</span> <span class="pl-k">===</span> <span class="pl-c1">0</span>;
  }
};

<span class="pl-c1">1</span> <span class="pl-k">instanceof</span> Even <span class="pl-c"><span class="pl-c">//</span> false</span>
<span class="pl-c1">2</span> <span class="pl-k">instanceof</span> Even <span class="pl-c"><span class="pl-c">//</span> true</span>
<span class="pl-c1">12345</span> <span class="pl-k">instanceof</span> Even <span class="pl-c"><span
                class="pl-c">//</span> false</span></pre>
    </div>
    <h3><a href="#symbolisconcatspreadable"  class="anchor"
           id="user-content-symbolisconcatspreadable">

    </a>Symbol.isConcatSpreadable
    </h3>
    <p>对象的<code>Symbol.isConcatSpreadable</code>属性等于一个布尔值，表示该对象用于<code>Array.prototype.concat()</code>时，是否可以展开。</p>
    <div class="highlight highlight-source-js"><pre><span class="pl-k">let</span> arr1 <span
            class="pl-k">=</span> [<span class="pl-s"><span class="pl-pds">'</span>c<span class="pl-pds">'</span></span>, <span
            class="pl-s"><span class="pl-pds">'</span>d<span class="pl-pds">'</span></span>];
[<span class="pl-s"><span class="pl-pds">'</span>a<span class="pl-pds">'</span></span>, <span class="pl-s"><span
                class="pl-pds">'</span>b<span class="pl-pds">'</span></span>].<span
                class="pl-c1">concat</span>(arr1, <span class="pl-s"><span class="pl-pds">'</span>e<span class="pl-pds">'</span></span>) <span
                class="pl-c"><span class="pl-c">//</span> ['a', 'b', 'c', 'd', 'e']</span>
arr1[<span class="pl-c1">Symbol</span>.<span class="pl-smi">isConcatSpreadable</span>] <span class="pl-c"><span
                class="pl-c">//</span> undefined</span>

<span class="pl-k">let</span> arr2 <span class="pl-k">=</span> [<span class="pl-s"><span class="pl-pds">'</span>c<span
                class="pl-pds">'</span></span>, <span class="pl-s"><span class="pl-pds">'</span>d<span
                class="pl-pds">'</span></span>];
arr2[<span class="pl-c1">Symbol</span>.<span class="pl-smi">isConcatSpreadable</span>] <span class="pl-k">=</span> <span
                class="pl-c1">false</span>;
[<span class="pl-s"><span class="pl-pds">'</span>a<span class="pl-pds">'</span></span>, <span class="pl-s"><span
                class="pl-pds">'</span>b<span class="pl-pds">'</span></span>].<span
                class="pl-c1">concat</span>(arr2, <span class="pl-s"><span class="pl-pds">'</span>e<span class="pl-pds">'</span></span>) <span
                class="pl-c"><span class="pl-c">//</span> ['a', 'b', ['c','d'], 'e']</span></pre>
    </div>
    <p>上面代码说明，数组的默认行为是可以展开，<code>Symbol.isConcatSpreadable</code>默认等于<code>undefined</code>。该属性等于<code>true</code>时，也有展开的效果。
    </p>
    <p>类似数组的对象正好相反，默认不展开。它的<code>Symbol.isConcatSpreadable</code>属性设为<code>true</code>，才可以展开。</p>
    <div class="highlight highlight-source-js"><pre><span class="pl-k">let</span> obj <span class="pl-k">=</span> {length<span
            class="pl-k">:</span> <span class="pl-c1">2</span>, <span class="pl-c1">0</span><span class="pl-k">:</span> <span
            class="pl-s"><span class="pl-pds">'</span>c<span class="pl-pds">'</span></span>, <span
            class="pl-c1">1</span><span class="pl-k">:</span> <span class="pl-s"><span class="pl-pds">'</span>d<span
            class="pl-pds">'</span></span>};
[<span class="pl-s"><span class="pl-pds">'</span>a<span class="pl-pds">'</span></span>, <span class="pl-s"><span
                class="pl-pds">'</span>b<span class="pl-pds">'</span></span>].<span
                class="pl-c1">concat</span>(obj, <span class="pl-s"><span class="pl-pds">'</span>e<span
                class="pl-pds">'</span></span>) <span class="pl-c"><span
                class="pl-c">//</span> ['a', 'b', obj, 'e']</span>

obj[<span class="pl-c1">Symbol</span>.<span class="pl-smi">isConcatSpreadable</span>] <span class="pl-k">=</span> <span
                class="pl-c1">true</span>;
[<span class="pl-s"><span class="pl-pds">'</span>a<span class="pl-pds">'</span></span>, <span class="pl-s"><span
                class="pl-pds">'</span>b<span class="pl-pds">'</span></span>].<span
                class="pl-c1">concat</span>(obj, <span class="pl-s"><span class="pl-pds">'</span>e<span
                class="pl-pds">'</span></span>) <span class="pl-c"><span class="pl-c">//</span> ['a', 'b', 'c', 'd', 'e']</span></pre>
    </div>
    <p><code>Symbol.isConcatSpreadable</code>属性也可以定义在类里面。</p>
    <div class="highlight highlight-source-js"><pre><span class="pl-k">class</span> <span class="pl-en">A1</span> <span
            class="pl-k">extends</span> <span class="pl-e">Array</span> {
  <span class="pl-en">constructor</span>(<span class="pl-smi">args</span>) {
    <span class="pl-c1">super</span>(args);
    <span class="pl-c1">this</span>[<span class="pl-c1">Symbol</span>.<span
                class="pl-smi">isConcatSpreadable</span>] <span class="pl-k">=</span> <span class="pl-c1">true</span>;
  }
}
<span class="pl-k">class</span> <span class="pl-en">A2</span> <span class="pl-k">extends</span> <span
                class="pl-e">Array</span> {
  <span class="pl-en">constructor</span>(<span class="pl-smi">args</span>) {
    <span class="pl-c1">super</span>(args);
  }
  <span class="pl-k">get</span> [<span class="pl-c1">Symbol</span>.<span class="pl-smi">isConcatSpreadable</span>] () {
    <span class="pl-k">return</span> <span class="pl-c1">false</span>;
  }
}
<span class="pl-k">let</span> a1 <span class="pl-k">=</span> <span class="pl-k">new</span> <span class="pl-en">A1</span>();
a1[<span class="pl-c1">0</span>] <span class="pl-k">=</span> <span class="pl-c1">3</span>;
a1[<span class="pl-c1">1</span>] <span class="pl-k">=</span> <span class="pl-c1">4</span>;
<span class="pl-k">let</span> a2 <span class="pl-k">=</span> <span class="pl-k">new</span> <span class="pl-en">A2</span>();
a2[<span class="pl-c1">0</span>] <span class="pl-k">=</span> <span class="pl-c1">5</span>;
a2[<span class="pl-c1">1</span>] <span class="pl-k">=</span> <span class="pl-c1">6</span>;
[<span class="pl-c1">1</span>, <span class="pl-c1">2</span>].<span class="pl-c1">concat</span>(a1).<span class="pl-c1">concat</span>(a2)
<span class="pl-c"><span class="pl-c">//</span> [1, 2, 3, 4, [5, 6]]</span></pre>
    </div>
    <p>上面代码中，类<code>A1</code>是可展开的，类<code>A2</code>是不可展开的，所以使用<code>concat</code>时有不一样的结果。</p>
    <p>注意，<code>Symbol.isConcatSpreadable</code>的位置差异，<code>A1</code>是定义在实例上，<code>A2</code>是定义在类本身，效果相同。</p>
    <h3><a href="#symbolspecies"  class="anchor" id="user-content-symbolspecies">

    </a>Symbol.species
    </h3>
    <p>对象的<code>Symbol.species</code>属性，指向当前对象的构造函数。创造实例时，默认会调用这个方法，即使用这个属性返回的函数当作构造函数，来创造新的实例对象。</p>
    <div class="highlight highlight-source-js"><pre><span class="pl-k">class</span> <span
            class="pl-en">MyArray</span> <span class="pl-k">extends</span> <span class="pl-e">Array</span> {
  <span class="pl-c"><span class="pl-c">//</span> 覆盖父类 Array 的构造函数</span>
  <span class="pl-k">static</span> <span class="pl-k">get</span> [<span class="pl-c1">Symbol</span>.<span
                class="pl-smi">species</span>]() { <span class="pl-k">return</span> <span class="pl-c1">Array</span>; }
}</pre>
    </div>
    <p>
        上面代码中，子类<code>MyArray</code>继承了父类<code>Array</code>。创建<code>MyArray</code>的实例对象时，本来会调用它自己的构造函数（本例中被省略了），但是由于定义了<code>Symbol.species</code>属性，所以会使用这个属性返回的的函数，创建<code>MyArray</code>的实例。
    </p>
    <p>这个例子也说明，定义<code>Symbol.species</code>属性要采用<code>get</code>读取器。默认的<code>Symbol.species</code>属性等同于下面的写法。</p>
    <div class="highlight highlight-source-js"><pre><span class="pl-k">static</span> <span
            class="pl-k">get</span> [<span class="pl-c1">Symbol</span>.<span class="pl-smi">species</span>]() {
  <span class="pl-k">return</span> <span class="pl-c1">this</span>;
}</pre>
    </div>
    <p>下面是一个例子。</p>
    <div class="highlight highlight-source-js"><pre><span class="pl-k">class</span> <span
            class="pl-en">MyArray</span> <span class="pl-k">extends</span> <span class="pl-e">Array</span> {
  <span class="pl-k">static</span> <span class="pl-k">get</span> [<span class="pl-c1">Symbol</span>.<span
                class="pl-smi">species</span>]() { <span class="pl-k">return</span> <span class="pl-c1">Array</span>; }
}
<span class="pl-k">let</span> a <span class="pl-k">=</span> <span class="pl-k">new</span> <span
                class="pl-en">MyArray</span>(<span class="pl-c1">1</span>,<span class="pl-c1">2</span>,<span
                class="pl-c1">3</span>);
<span class="pl-k">let</span> mapped <span class="pl-k">=</span> <span class="pl-smi">a</span>.<span
                class="pl-en">map</span>(<span class="pl-smi">x</span> <span class="pl-k">=&gt;</span> x <span
                class="pl-k">*</span> x);

mapped <span class="pl-k">instanceof</span> MyArray <span class="pl-c"><span class="pl-c">//</span> false</span>
mapped <span class="pl-k">instanceof</span> <span class="pl-c1">Array</span> <span class="pl-c"><span
                class="pl-c">//</span> true</span></pre>
    </div>
    <p>上面代码中，由于构造函数被替换成了<code>Array</code>。所以，<code>mapped</code>对象不是<code>MyArray</code>的实例，而是<code>Array</code>的实例。
    </p>
    <h3><a href="#symbolmatch"  class="anchor" id="user-content-symbolmatch">

    </a>Symbol.match
    </h3>
    <p>对象的<code>Symbol.match</code>属性，指向一个函数。当执行<code>str.match(myObject)</code>时，如果该属性存在，会调用它，返回该方法的返回值。</p>
    <div class="highlight highlight-source-js"><pre><span class="pl-c1">String</span>.<span
            class="pl-c1">prototype</span>.<span class="pl-c1">match</span>(regexp)
<span class="pl-c"><span class="pl-c">//</span> 等同于</span>
regexp[<span class="pl-c1">Symbol</span>.<span class="pl-smi">match</span>](<span class="pl-c1">this</span>)

<span class="pl-k">class</span> <span class="pl-en">MyMatcher</span> {
  [<span class="pl-c1">Symbol</span>.<span class="pl-smi">match</span>](<span class="pl-smi">string</span>) {
    <span class="pl-k">return</span> <span class="pl-s"><span class="pl-pds">'</span>hello world<span
                class="pl-pds">'</span></span>.<span class="pl-c1">indexOf</span>(string);
  }
}

<span class="pl-s"><span class="pl-pds">'</span>e<span class="pl-pds">'</span></span>.<span
                class="pl-c1">match</span>(<span class="pl-k">new</span> <span class="pl-en">MyMatcher</span>()) <span
                class="pl-c"><span class="pl-c">//</span> 1</span></pre>
    </div>
    <h3><a href="#symbolreplace"  class="anchor" id="user-content-symbolreplace">

    </a>Symbol.replace
    </h3>
    <p>对象的<code>Symbol.replace</code>属性，指向一个方法，当该对象被<code>String.prototype.replace</code>方法调用时，会返回该方法的返回值。</p>
    <div class="highlight highlight-source-js"><pre><span class="pl-c1">String</span>.<span
            class="pl-c1">prototype</span>.<span class="pl-c1">replace</span>(searchValue, replaceValue)
<span class="pl-c"><span class="pl-c">//</span> 等同于</span>
searchValue[<span class="pl-c1">Symbol</span>.<span class="pl-smi">replace</span>](<span class="pl-c1">this</span>, replaceValue)</pre>
    </div>
    <p>下面是一个例子。</p>
    <div class="highlight highlight-source-js"><pre><span class="pl-k">const</span> <span class="pl-c1">x</span> <span
            class="pl-k">=</span> {};
x[<span class="pl-c1">Symbol</span>.<span class="pl-smi">replace</span>] <span class="pl-k">=</span> (<span
                class="pl-k">...</span><span class="pl-v">s</span>) <span class="pl-k">=&gt;</span> <span class="pl-en">console</span>.<span
                class="pl-c1">log</span>(s);

<span class="pl-s"><span class="pl-pds">'</span>Hello<span class="pl-pds">'</span></span>.<span
                class="pl-c1">replace</span>(x, <span class="pl-s"><span class="pl-pds">'</span>World<span
                class="pl-pds">'</span></span>) <span class="pl-c"><span
                class="pl-c">//</span> ["Hello", "World"]</span></pre>
    </div>
    <p><code>Symbol.replace</code>方法会收到两个参数，第一个参数是<code>replace</code>方法正在作用的对象，上面例子是<code>Hello</code>，第二个参数是替换后的值，上面例子是<code>World</code>。
    </p>
    <h3><a href="#symbolsearch"  class="anchor" id="user-content-symbolsearch">

    </a>Symbol.search
    </h3>
    <p>对象的<code>Symbol.search</code>属性，指向一个方法，当该对象被<code>String.prototype.search</code>方法调用时，会返回该方法的返回值。</p>
    <div class="highlight highlight-source-js"><pre><span class="pl-c1">String</span>.<span
            class="pl-c1">prototype</span>.<span class="pl-c1">search</span>(regexp)
<span class="pl-c"><span class="pl-c">//</span> 等同于</span>
regexp[<span class="pl-c1">Symbol</span>.<span class="pl-smi">search</span>](<span class="pl-c1">this</span>)

<span class="pl-k">class</span> <span class="pl-en">MySearch</span> {
  <span class="pl-en">constructor</span>(<span class="pl-smi">value</span>) {
    <span class="pl-c1">this</span>.<span class="pl-c1">value</span> <span class="pl-k">=</span> value;
  }
  [<span class="pl-c1">Symbol</span>.<span class="pl-smi">search</span>](<span class="pl-smi">string</span>) {
    <span class="pl-k">return</span> <span class="pl-smi">string</span>.<span class="pl-c1">indexOf</span>(<span
                class="pl-c1">this</span>.<span class="pl-c1">value</span>);
  }
}
<span class="pl-s"><span class="pl-pds">'</span>foobar<span class="pl-pds">'</span></span>.<span
                class="pl-c1">search</span>(<span class="pl-k">new</span> <span class="pl-en">MySearch</span>(<span
                class="pl-s"><span class="pl-pds">'</span>foo<span class="pl-pds">'</span></span>)) <span
                class="pl-c"><span class="pl-c">//</span> 0</span></pre>
    </div>
    <h3><a href="#symbolsplit"  class="anchor" id="user-content-symbolsplit">

    </a>Symbol.split
    </h3>
    <p>对象的<code>Symbol.split</code>属性，指向一个方法，当该对象被<code>String.prototype.split</code>方法调用时，会返回该方法的返回值。</p>
    <div class="highlight highlight-source-js"><pre><span class="pl-c1">String</span>.<span
            class="pl-c1">prototype</span>.<span class="pl-c1">split</span>(separator, limit)
<span class="pl-c"><span class="pl-c">//</span> 等同于</span>
separator[<span class="pl-c1">Symbol</span>.<span class="pl-smi">split</span>](<span
                class="pl-c1">this</span>, limit)</pre>
    </div>
    <p>下面是一个例子。</p>
    <div class="highlight highlight-source-js"><pre><span class="pl-k">class</span> <span
            class="pl-en">MySplitter</span> {
  <span class="pl-en">constructor</span>(<span class="pl-smi">value</span>) {
    <span class="pl-c1">this</span>.<span class="pl-c1">value</span> <span class="pl-k">=</span> value;
  }
  [<span class="pl-c1">Symbol</span>.<span class="pl-smi">split</span>](<span class="pl-smi">string</span>) {
    <span class="pl-k">let</span> index <span class="pl-k">=</span> <span class="pl-smi">string</span>.<span
                class="pl-c1">indexOf</span>(<span class="pl-c1">this</span>.<span class="pl-c1">value</span>);
    <span class="pl-k">if</span> (index <span class="pl-k">===</span> <span class="pl-k">-</span><span
                class="pl-c1">1</span>) {
      <span class="pl-k">return</span> string;
    }
    <span class="pl-k">return</span> [
      <span class="pl-smi">string</span>.<span class="pl-c1">substr</span>(<span class="pl-c1">0</span>, index),
      <span class="pl-smi">string</span>.<span class="pl-c1">substr</span>(index <span class="pl-k">+</span> <span
                class="pl-c1">this</span>.<span class="pl-c1">value</span>.<span class="pl-c1">length</span>)
    ];
  }
}

<span class="pl-s"><span class="pl-pds">'</span>foobar<span class="pl-pds">'</span></span>.<span
                class="pl-c1">split</span>(<span class="pl-k">new</span> <span class="pl-en">MySplitter</span>(<span
                class="pl-s"><span class="pl-pds">'</span>foo<span class="pl-pds">'</span></span>))
<span class="pl-c"><span class="pl-c">//</span> ['', 'bar']</span>

<span class="pl-s"><span class="pl-pds">'</span>foobar<span class="pl-pds">'</span></span>.<span
                class="pl-c1">split</span>(<span class="pl-k">new</span> <span class="pl-en">MySplitter</span>(<span
                class="pl-s"><span class="pl-pds">'</span>bar<span class="pl-pds">'</span></span>))
<span class="pl-c"><span class="pl-c">//</span> ['foo', '']</span>

<span class="pl-s"><span class="pl-pds">'</span>foobar<span class="pl-pds">'</span></span>.<span
                class="pl-c1">split</span>(<span class="pl-k">new</span> <span class="pl-en">MySplitter</span>(<span
                class="pl-s"><span class="pl-pds">'</span>baz<span class="pl-pds">'</span></span>))
<span class="pl-c"><span class="pl-c">//</span> 'foobar'</span></pre>
    </div>
    <p>上面方法使用<code>Symbol.split</code>方法，重新定义了字符串对象的<code>split</code>方法的行为，</p>
    <h3><a href="#symboliterator"  class="anchor" id="user-content-symboliterator">

    </a>Symbol.iterator
    </h3>
    <p>对象的<code>Symbol.iterator</code>属性，指向该对象的默认遍历器方法。</p>
    <div class="highlight highlight-source-js"><pre><span class="pl-k">const</span> <span
            class="pl-c1">myIterable</span> <span class="pl-k">=</span> {};
myIterable[<span class="pl-c1">Symbol</span>.<span class="pl-smi">iterator</span>] <span class="pl-k">=</span> <span
                class="pl-k">function</span><span class="pl-k">*</span> () {
  <span class="pl-k">yield</span> <span class="pl-c1">1</span>;
  <span class="pl-k">yield</span> <span class="pl-c1">2</span>;
  <span class="pl-k">yield</span> <span class="pl-c1">3</span>;
};

[<span class="pl-k">...</span>myIterable] <span class="pl-c"><span class="pl-c">//</span> [1, 2, 3]</span></pre>
    </div>
    <p>对象进行<code>for...of</code>循环时，会调用<code>Symbol.iterator</code>方法，返回该对象的默认遍历器，详细介绍参见《Iterator 和 for...of 循环》一章。</p>
    <div class="highlight highlight-source-js"><pre><span class="pl-k">class</span> <span
            class="pl-en">Collection</span> {
  <span class="pl-k">*</span>[<span class="pl-c1">Symbol</span>.<span class="pl-smi">iterator</span>]() {
    <span class="pl-k">let</span> i <span class="pl-k">=</span> <span class="pl-c1">0</span>;
    <span class="pl-k">while</span>(<span class="pl-c1">this</span>[i] <span class="pl-k">!==</span> <span
                class="pl-c1">undefined</span>) {
      <span class="pl-k">yield</span> <span class="pl-c1">this</span>[i];
      <span class="pl-k">++</span>i;
    }
  }
}

<span class="pl-k">let</span> myCollection <span class="pl-k">=</span> <span class="pl-k">new</span> <span
                class="pl-en">Collection</span>();
myCollection[<span class="pl-c1">0</span>] <span class="pl-k">=</span> <span class="pl-c1">1</span>;
myCollection[<span class="pl-c1">1</span>] <span class="pl-k">=</span> <span class="pl-c1">2</span>;

<span class="pl-k">for</span>(<span class="pl-k">let</span> value <span class="pl-k">of</span> myCollection) {
  <span class="pl-en">console</span>.<span class="pl-c1">log</span>(value);
}
<span class="pl-c"><span class="pl-c">//</span> 1</span>
<span class="pl-c"><span class="pl-c">//</span> 2</span></pre>
    </div>
    <h3><a href="#symboltoprimitive"  class="anchor" id="user-content-symboltoprimitive">

    </a>Symbol.toPrimitive
    </h3>
    <p>对象的<code>Symbol.toPrimitive</code>属性，指向一个方法。该对象被转为原始类型的值时，会调用这个方法，返回该对象对应的原始类型值。</p>
    <p><code>Symbol.toPrimitive</code>被调用时，会接受一个字符串参数，表示当前运算的模式，一共有三种模式。</p>
    <ul>
        <li>Number：该场合需要转成数值</li>
        <li>String：该场合需要转成字符串</li>
        <li>Default：该场合可以转成数值，也可以转成字符串</li>
    </ul>
    <div class="highlight highlight-source-js"><pre><span class="pl-k">let</span> obj <span class="pl-k">=</span> {
  [<span class="pl-c1">Symbol</span>.<span class="pl-smi">toPrimitive</span>](<span class="pl-smi">hint</span>) {
    <span class="pl-k">switch</span> (hint) {
      <span class="pl-k">case</span> <span class="pl-s"><span class="pl-pds">'</span>number<span class="pl-pds">'</span></span>:
        <span class="pl-k">return</span> <span class="pl-c1">123</span>;
      <span class="pl-k">case</span> <span class="pl-s"><span class="pl-pds">'</span>string<span class="pl-pds">'</span></span>:
        <span class="pl-k">return</span> <span class="pl-s"><span class="pl-pds">'</span>str<span
                class="pl-pds">'</span></span>;
      <span class="pl-k">case</span> <span class="pl-s"><span class="pl-pds">'</span>default<span
                class="pl-pds">'</span></span>:
        <span class="pl-k">return</span> <span class="pl-s"><span class="pl-pds">'</span>default<span
                class="pl-pds">'</span></span>;
      <span class="pl-k">default</span>:
        <span class="pl-k">throw</span> <span class="pl-k">new</span> <span class="pl-en">Error</span>();
     }
   }
};

<span class="pl-c1">2</span> <span class="pl-k">*</span> obj <span class="pl-c"><span class="pl-c">//</span> 246</span>
<span class="pl-c1">3</span> <span class="pl-k">+</span> obj <span class="pl-c"><span class="pl-c">//</span> '3default'</span>
obj <span class="pl-k">==</span> <span class="pl-s"><span class="pl-pds">'</span>default<span
                class="pl-pds">'</span></span> <span class="pl-c"><span class="pl-c">//</span> true</span>
<span class="pl-c1">String</span>(obj) <span class="pl-c"><span class="pl-c">//</span> 'str'</span></pre>
    </div>
    <h3><a href="#symboltostringtag"  class="anchor" id="user-content-symboltostringtag">

    </a>Symbol.toStringTag
    </h3>
    <p>
        对象的<code>Symbol.toStringTag</code>属性，指向一个方法。在该对象上面调用<code>Object.prototype.toString</code>方法时，如果这个属性存在，它的返回值会出现在<code>toString</code>方法返回的字符串之中，表示对象的类型。也就是说，这个属性可以用来定制<code>[object
        Object]</code>或<code>[object Array]</code>中<code>object</code>后面的那个字符串。</p>
    <div class="highlight highlight-source-js"><pre><span class="pl-c"><span class="pl-c">//</span> 例一</span>
({[<span class="pl-c1">Symbol</span>.<span class="pl-smi">toStringTag</span>]<span class="pl-k">:</span> <span
                class="pl-s"><span class="pl-pds">'</span>Foo<span class="pl-pds">'</span></span>}.<span class="pl-c1">toString</span>())
<span class="pl-c"><span class="pl-c">//</span> "[object Foo]"</span>

<span class="pl-c"><span class="pl-c">//</span> 例二</span>
<span class="pl-k">class</span> <span class="pl-en">Collection</span> {
  <span class="pl-k">get</span> [<span class="pl-c1">Symbol</span>.<span class="pl-smi">toStringTag</span>]() {
    <span class="pl-k">return</span> <span class="pl-s"><span class="pl-pds">'</span>xxx<span
                class="pl-pds">'</span></span>;
  }
}
<span class="pl-k">let</span> x <span class="pl-k">=</span> <span class="pl-k">new</span> <span
                class="pl-en">Collection</span>();
<span class="pl-c1">Object</span>.<span class="pl-c1">prototype</span>.<span class="pl-smi">toString</span>.<span
                class="pl-c1">call</span>(x) <span class="pl-c"><span class="pl-c">//</span> "[object xxx]"</span></pre>
    </div>
    <p>ES6 新增内置对象的<code>Symbol.toStringTag</code>属性值如下。</p>
    <ul>
        <li><code>JSON[Symbol.toStringTag]</code>：'JSON'</li>
        <li><code>Math[Symbol.toStringTag]</code>：'Math'</li>
        <li>Module 对象<code>M[Symbol.toStringTag]</code>：'Module'</li>
        <li><code>ArrayBuffer.prototype[Symbol.toStringTag]</code>：'ArrayBuffer'</li>
        <li><code>DataView.prototype[Symbol.toStringTag]</code>：'DataView'</li>
        <li><code>Map.prototype[Symbol.toStringTag]</code>：'Map'</li>
        <li><code>Promise.prototype[Symbol.toStringTag]</code>：'Promise'</li>
        <li><code>Set.prototype[Symbol.toStringTag]</code>：'Set'</li>
        <li><code>%TypedArray%.prototype[Symbol.toStringTag]</code>：'Uint8Array'等</li>
        <li><code>WeakMap.prototype[Symbol.toStringTag]</code>：'WeakMap'</li>
        <li><code>WeakSet.prototype[Symbol.toStringTag]</code>：'WeakSet'</li>
        <li><code>%MapIteratorPrototype%[Symbol.toStringTag]</code>：'Map Iterator'</li>
        <li><code>%SetIteratorPrototype%[Symbol.toStringTag]</code>：'Set Iterator'</li>
        <li><code>%StringIteratorPrototype%[Symbol.toStringTag]</code>：'String Iterator'</li>
        <li><code>Symbol.prototype[Symbol.toStringTag]</code>：'Symbol'</li>
        <li><code>Generator.prototype[Symbol.toStringTag]</code>：'Generator'</li>
        <li><code>GeneratorFunction.prototype[Symbol.toStringTag]</code>：'GeneratorFunction'</li>
    </ul>
    <h3><a href="#symbolunscopables"  class="anchor" id="user-content-symbolunscopables">

    </a>Symbol.unscopables
    </h3>
    <p>对象的<code>Symbol.unscopables</code>属性，指向一个对象。该对象指定了使用<code>with</code>关键字时，哪些属性会被<code>with</code>环境排除。</p>
    <div class="highlight highlight-source-js"><pre><span class="pl-c1">Array</span>.<span
            class="pl-c1">prototype</span>[<span class="pl-c1">Symbol</span>.<span class="pl-smi">unscopables</span>]
<span class="pl-c"><span class="pl-c">//</span> {</span>
<span class="pl-c"><span class="pl-c">//</span>   copyWithin: true,</span>
<span class="pl-c"><span class="pl-c">//</span>   entries: true,</span>
<span class="pl-c"><span class="pl-c">//</span>   fill: true,</span>
<span class="pl-c"><span class="pl-c">//</span>   find: true,</span>
<span class="pl-c"><span class="pl-c">//</span> &nbsp; findIndex: true,</span>
<span class="pl-c"><span class="pl-c">//</span>   includes: true,</span>
<span class="pl-c"><span class="pl-c">//</span>   keys: true</span>
<span class="pl-c"><span class="pl-c">//</span> }</span>

<span class="pl-c1">Object</span>.<span class="pl-c1">keys</span>(<span class="pl-c1">Array</span>.<span class="pl-c1">prototype</span>[<span
                class="pl-c1">Symbol</span>.<span class="pl-smi">unscopables</span>])
<span class="pl-c"><span class="pl-c">//</span> ['copyWithin', 'entries', 'fill', 'find', 'findIndex', 'includes', 'keys']</span></pre>
    </div>
    <p>上面代码说明，数组有 7 个属性，会被<code>with</code>命令排除。</p>
    <div class="highlight highlight-source-js"><pre><span class="pl-c"><span
            class="pl-c">//</span> 没有 unscopables 时</span>
<span class="pl-k">class</span> <span class="pl-en">MyClass</span> {
  <span class="pl-en">foo</span>() { <span class="pl-k">return</span> <span class="pl-c1">1</span>; }
}

<span class="pl-k">var</span> <span class="pl-en">foo</span> <span class="pl-k">=</span> <span
                class="pl-k">function</span> () { <span class="pl-k">return</span> <span class="pl-c1">2</span>; };

<span class="pl-k">with</span> (<span class="pl-smi">MyClass</span>.<span class="pl-c1">prototype</span>) {
  <span class="pl-en">foo</span>(); <span class="pl-c"><span class="pl-c">//</span> 1</span>
}

<span class="pl-c"><span class="pl-c">//</span> 有 unscopables 时</span>
<span class="pl-k">class</span> <span class="pl-en">MyClass</span> {
  <span class="pl-en">foo</span>() { <span class="pl-k">return</span> <span class="pl-c1">1</span>; }
  <span class="pl-k">get</span> [<span class="pl-c1">Symbol</span>.<span class="pl-smi">unscopables</span>]() {
    <span class="pl-k">return</span> { foo<span class="pl-k">:</span> <span class="pl-c1">true</span> };
  }
}

<span class="pl-k">var</span> <span class="pl-en">foo</span> <span class="pl-k">=</span> <span
                class="pl-k">function</span> () { <span class="pl-k">return</span> <span class="pl-c1">2</span>; };

<span class="pl-k">with</span> (<span class="pl-smi">MyClass</span>.<span class="pl-c1">prototype</span>) {
  <span class="pl-en">foo</span>(); <span class="pl-c"><span class="pl-c">//</span> 2</span>
}</pre>
    </div>
    <p>上面代码通过指定<code>Symbol.unscopables</code>属性，使得<code>with</code>语法块不会在当前作用域寻找<code>foo</code>属性，即<code>foo</code>将指向外层作用域的变量。
    </p>
</div>
</body>
</html>
